<!DOCTYPE html>
<html lang="zh-CN">

<meta name="viewport" content="width=device-width,initial-scale=1" />
<meta name="description" content="Fastllm Web Interface" />

<head>
    <meta charset="utf-8">
    <title>Fastllm Web Interface</title>
    <style>
        * {
            box-sizing: border-box;
            font-family: system-ui,-apple-system,BlinkMacSystemFont,segoe ui,Roboto, Helvetica,Arial, sans-serif,apple color emoji,segoe ui emoji,segoe ui symbol;
        }

        body {
            background-color: #edeff2;
        }

        .chat_window {
            position: absolute;
            width: calc(100% - 6px);
            max-width: 1100px;
            height: calc(100% - 6px);
            max-height: 888px;
            border-radius: 8px;
            background-color: #fff;
            left: 50%;
            top: 50%;
            overflow: hidden;
            transform: translate(-50%, -50%);
            box-shadow: 0 10px 20px rgba(0, 0, 0, 0.15);
        }

        .top_menu {
            background-color: #f6f6f6;
            width: 100%;
            height: 50px;
            padding: 12px 0;
        }

        .top_menu .buttons {
            margin: 5px 0 0 20px;
            position: absolute;
            font-size: 0;
        }

        .top_menu .buttons .button {
            width: 16px;
            height: 16px;
            border-radius: 50%;
            display: inline-block;
            margin-right: 10px;
            position: relative;
        }

        .top_menu .buttons .button.close {
            background-color: #e15b64;
        }

        .top_menu .buttons .button.minimize {
            background-color: #f8b26a;
        }

        .top_menu .buttons .button.maximize {
            background-color: #99c959;
        }

        .top_menu .title {
            text-align: center;
            color: #909090;
            font-size: 20px;
            line-height: 26px;
        }

        .messages {
            position: relative;
            height: calc(100% - 117px);
            overflow-x: hidden;
            overflow-y: auto;
        }

        .messages::-webkit-scrollbar {
            width: 10px;
            height: 10px;
        }

        .messages::-webkit-scrollbar-track {
            background-clip: padding-box;
            background: transparent;
            border: solid transparent;
            border-width: 1px;
        }

        .messages::-webkit-scrollbar-corner {
            background-color: transparent;
        }

        .messages::-webkit-scrollbar-thumb {
            background-color: rgba(0, 0, 0, 0.1);
            background-clip: padding-box;
            border: solid transparent;
            border-radius: 10px;
        }

        .messages::-webkit-scrollbar-thumb:hover {
            background-color: rgba(0, 0, 0, 0.4);
        }

        .messages .message {
            clear: both;
            overflow: hidden;
            margin-bottom: 20px;
            transition: all 0.5s linear;
            opacity: 0;
        }

        .messages .message.left .avatar {
            background-color: #f5886e;
            float: left;
        }

        .messages .message.left .text {
            color: #c48843;
        }

        .messages .message.right .avatar {
            background-color: #fdbf68;
            float: right;
        }

        .messages .message.right .text {
            color: #45829b;
        }

        .messages .message.appeared {
            opacity: 1;
        }

        .messages .message .avatar {
            width: 60px;
            height: 60px;
            border-radius: 50%;
            display: inline-block;
        }

        .messages {
            font-size: 16px;
            color: #343541;
            text-align: center;
        }

        #chatlog {
            word-wrap: break-word;
            text-align: start;
        }

        #chatlog>div {
            padding: 18px 25px;
        }

        #chatlog .request {
            position: relative;
        }

        #chatlog .response {
            background: #f7f7f8;
            position: relative;
        }

        .response .markdown-body {
            background: #f7f7f8 !important;
        }

        #chatlog .markdown-body>pre {
            overflow-x: auto;
            padding: 10px;
            position: relative;
            background: rgba(180, 180, 180, 0.1);
        }

        #chatlog .markdown-body>pre::-webkit-scrollbar {
            width: 10px;
            height: 10px;
        }

        #chatlog .response>pre::-webkit-scrollbar-track {
            background-clip: padding-box;
            background: transparent;
            border: solid transparent;
            border-width: 1px;
        }

        #chatlog .response>pre::-webkit-scrollbar-corner {
            background-color: transparent;
        }

        #chatlog .response>pre::-webkit-scrollbar-thumb {
            background-color: rgba(0, 0, 0, 0.1);
            background-clip: padding-box;
            border: solid transparent;
            border-radius: 10px;
        }

        #chatlog .response>pre::-webkit-scrollbar-thumb:hover {
            background-color: rgba(0, 0, 0, 0.4);
        }

        .m-mdic-copy-wrapper {
            position: absolute;
            top: 5px;
            right: 16px;
        }

        .m-mdic-copy-wrapper span.u-mdic-copy-code_lang {
            position: absolute;
            top: 3px;
            right: calc(100% + 4px);
            font-family: system-ui;
            font-size: 12px;
            line-height: 18px;
            color: #555;
            opacity: 0.3;
        }

        .m-mdic-copy-wrapper div.u-mdic-copy-notify {
            position: absolute;
            top: 0;
            right: 0;
            padding: 3px 6px;
            border: 0;
            border-radius: 3px;
            background: none;
            font-family: system-ui;
            font-size: 12px;
            line-height: 18px;
            color: #555;
            opacity: 0.3;
            outline: none;
            opacity: 1;
            right: 100%;
            padding-right: 4px;
        }

        .m-mdic-copy-wrapper button.u-mdic-copy-btn {
            position: relative;
            top: 0;
            right: 0;
            padding: 3px 6px;
            border: 0;
            border-radius: 3px;
            background: none;
            font-family: system-ui;
            font-size: 12px;
            line-height: 18px;
            color: #555;
            opacity: 0.3;
            outline: none;
            cursor: pointer;
            transition: opacity 0.2s;
        }

        .m-mdic-copy-wrapper button.u-mdic-copy-btn:hover {
            opacity: 1;
        }

        #stopChat {
            display: none;
            margin: 0 auto;
            margin-top: 3px;
            width: 80px;
            height: 32px;
            text-align: center;
            line-height: 32px;
            color: white;
            background: #f8b26a;
            cursor: pointer;
            border-radius: 3px;
            position: sticky;
            bottom: 2px;
            justify-content: center;
            align-items: center;
        }

        #stopChat>svg {
            margin-right: 8px;
        }

        #stopChat:hover {
            background: #f0aa60;
        }

        .bottom_wrapper {
            position: relative;
            width: 100%;
            background-color: #fff;
            padding: 10px 10px;
            position: absolute;
            bottom: 0;
        }

        .bottom_wrapper .message_input_wrapper {
            border: none;
            width: calc(100% - 143px);
            position: relative;
            text-align: left;
        }

        .bottom_wrapper .message_input_wrapper .message_input_text {
            border-radius: 4px;
            border: none;
            outline: none;
            resize: none;
            background: #eff1f1;
            color: #24292f;
            height: 47px;
            font-size: 16px;
            max-height: 200px;
            padding: 13px 0 13px 16px;
            width: 100%;
            display: block;
            transition: background 0.3s;
        }

        .bottom_wrapper .message_input_wrapper .message_input_text:focus {
            background: #e7e9eb;
        }

        .bottom_wrapper .message_input_wrapper .message_input_text::-webkit-scrollbar {
            display: none;
            width: 0;
            height: 0;
        }

        #sendbutton {
            width: 80px;
            height: 47px;
            font-size: 18px;
            font-weight: bold;
            border-radius: 3px;
            background-color: #b8da8b;
            border: none;
            padding: 0;
            color: #fff;
            cursor: pointer;
            transition: all 0.2s linear;
            text-align: center;
            float: right;
            position: absolute;
            right: 65px;
            bottom: 10px;
            cursor: not-allowed;
        }

        .activeSendBtn {
            background-color: #99c959 !important;
            cursor: pointer !important;
        }

        .activeSendBtn:hover {
            background-color: #90c050 !important;
        }

        #clearConv {
            position: absolute;
            right: 10px;
            bottom: 10px;
            width: 47px;
            height: 47px;
            font-size: 16px;
            display: inline-block;
            border-radius: 3px;
            background-color: #909090;
            border: 2px solid #909090;
            color: #fff;
            cursor: pointer;
            transition: all 0.2s linear;
            text-align: center;
        }

        #clearConv:hover {
            background-color: gray;
            border: 2px solid gray;
        }

        .loaded>span {
            display: inline-block;
        }

        .loaded>svg {
            display: none;
        }

        .loading {
            background: #e7e7e8 !important;
        }

        .loading>span {
            display: none;
        }

        .loading>svg {
            display: block;
        }

        .switch-slide {
            display: inline-block;
            vertical-align: middle;
        }

        .switch-slide-label {
            display: block;
            width: 38px;
            height: 18px;
            background: #909090;
            border-radius: 30px;
            cursor: pointer;
            position: relative;
            -webkit-transition: 0.3s ease;
            transition: 0.3s ease;
        }

        .switch-slide-label:after {
            content: "";
            display: block;
            width: 16px;
            height: 16px;
            border-radius: 100%;
            background: #fff;
            box-shadow: 0 1px 1px rgba(0, 0, 0, .1);
            position: absolute;
            left: 1px;
            top: 1px;
            -webkit-transform: translateZ(0);
            transform: translateZ(0);
            -webkit-transition: 0.3s ease;
            transition: 0.3s ease;
        }

        .switch-slide input:checked+label {
            background: #99c959;
            transition: 0.3s ease;
        }

        .switch-slide input:checked+label:after {
            left: 21px;
        }

        #setting {
            position: absolute;
            right: 15px;
            top: 5px;
            cursor: pointer;
            padding: 5px;
            border: none;
            background-color: transparent;
            border-radius: 4px;
        }

        #setting:hover {
            background: #e7e7e8;
        }

        .showSetting {
            background: #b0b0b0 !important;
        }

        #setDialog {
            color: #303030;
            position: absolute;
            z-index: 2;
            background: #f4f4f4;
            width: 320px;
            right: 6px;
            top: 46px;
            -webkit-user-select: none;
            user-select: none;
            border-radius: 5px;
            padding: 8px 12px 8px 12px;
        }

        #setDialog>div {
            margin-bottom: 4px;
        }

        #setDialog input {
            width: 100%;
        }

        #setDialog .inlineTitle {
            display: inline-block;
            line-height: 16px;
            vertical-align: middle;
        }

        #speechOption {
            margin-top: 6px;
        }

        #speechOption>div {
            margin-bottom: 4px;
        }

        .inputTextClass {
            outline: none;
            border-radius: 2px;
            margin-top: 2px;
            height: 32px;
            font-size: 15px;
            padding-left: 6px;
            background: white;
            border: none;
        }

        .areaTextClass {
            width: 100%;
            height: 80px;
            display: block;
            resize: none;
            padding: 6px;
        }

        input[type="range"] {
            -webkit-appearance: none;
            display: block;
            margin: 6px 0 3px 0;
            height: 8px;
            background: #909090;
            border-radius: 5px;
            background-image: linear-gradient(#99c959, #99c959);
            background-size: 100% 100%;
            background-repeat: no-repeat;
        }

        input[type="range"]::-webkit-slider-thumb {
            -webkit-appearance: none;
            height: 15px;
            width: 15px;
            border-radius: 50%;
            background: #99c959;
            cursor: ew-resize;
            box-shadow: 0 0 2px 0 #555;
        }

        input[type=range]::-webkit-slider-runnable-track {
            -webkit-appearance: none;
            box-shadow: none;
            border: none;
            background: transparent;
        }

        .justSetLine {
            display: flex;
            justify-content: space-between;
        }

        .presetSelect>div {
            display: inline-block;
        }

        .presetSelect select {
            outline: none;
            border-radius: 3px;
            width: 100px;
            border-color: rgba(0, 0, 0, .3);
        }

        .selectDef {
            display: flex;
            justify-content: space-between;
            font-size: 13px;
            color: #707070;
        }

        #preSetSpeech {
            width: 100%;
            outline: none;
            margin-top: 5px;
            border-radius: 3px;
            border-color: rgba(0, 0, 0, .3);
        }

        .mdOption {
            position: absolute;
            right: 5px;
            bottom: 0px;
            pointer-events: none;
        }

        .mdOption>div {
            margin-top: 4px;
            pointer-events: auto;
            cursor: pointer;
        }

        .mdOption>div>svg {
            color: #e3e3e3;
            display: block;
        }

        .mdOption svg:hover {
            color: #989898;
        }

        .mdOption svg * {
            pointer-events: none;
        }

        .refreshReq svg:not(:first-child) {
            display: none;
        }

        .halfRefReq svg:not(:nth-child(2)) {
            display: none;
        }

        .moreOption {
            position: relative;
        }

        .optionItems {
            position: absolute;
            top: -8px;
            display: flex;
            justify-content: space-between;
            visibility: hidden;
            z-index: 1;
            color: #808080;
        }

        .moreOptionHidden>div {
            display: none !important;
        }

        .optionItems:hover {
            visibility: visible;
        }

        .optionItems:hover .optionItem {
            transform: scale(1);
            visibility: visible;
        }

        .optionTrigger:hover+.optionItems .optionItem:nth-of-type(3) {
            transform: scale(1);
            visibility: visible;
            transition-delay: 50ms;
            transition-duration: 60ms;
        }

        .optionTrigger:hover+.optionItems .optionItem:nth-of-type(2) {
            visibility: visible;
            transition-delay: 80ms;
            transform: scale(1);
            transition-duration: 60ms;
        }

        .optionTrigger:hover+.optionItems .optionItem:nth-of-type(1) {
            visibility: visible;
            transition-delay: 110ms;
            transform: scale(1);
            transition-duration: 60ms;
        }

        .optionItem {
            border-radius: 3px;
            height: 30px;
            width: 30px;
            background-color: #f7f7f8;
            visibility: hidden;
            transform: scale(0);
            display: flex !important;
            justify-content: center;
            align-items: center;
        }

        .optionItem * {
            pointer-events: none;
        }

        .optionItem:hover {
            background: #e0e0e0;
        }
    </style>
</head>

<body>
    <div>
        <div class="chat_window">
            <div class="top_menu">
                <div class="buttons">
                    <div class="button close"></div>
                    <div class="button minimize"></div>
                    <div class="button maximize"></div>
                </div>
                <div class="title">Fastllm</div>
            </div>
            <div class="messages">
                <div id="chatlog"></div>
                <div id="stopChat"><svg width="24" height="24">
                        <use xlink:href="#stopResIcon" />
                    </svg>停止</div>
            </div>
            <div class="bottom_wrapper clearfix">
                <div class="message_input_wrapper">
                    <textarea class="message_input_text" spellcheck="false" placeholder="来问点什么吧"
                        id="chatinput"></textarea>
                </div>
                <button class="loaded" id="sendbutton">
                    <span>发送</span>
                    <svg style="margin:0 auto;height:40px;" viewBox="0 0 200 100" preserveAspectRatio="xMidYMid">
                        <g transform="translate(50 50)">
                            <circle cx="0" cy="0" r="15" fill="#e15b64">
                                <animateTransform attributeName="transform" type="scale" begin="-0.3333333333333333s"
                                    calcMode="spline" keySplines="0.3 0 0.7 1;0.3 0 0.7 1" values="0;1;0"
                                    keyTimes="0;0.5;1" dur="1s" repeatCount="indefinite"></animateTransform>
                            </circle>
                        </g>
                        <g transform="translate(100 50)">
                            <circle cx="0" cy="0" r="15" fill="#f8b26a">
                                <animateTransform attributeName="transform" type="scale" begin="-0.16666666666666666s"
                                    calcMode="spline" keySplines="0.3 0 0.7 1;0.3 0 0.7 1" values="0;1;0"
                                    keyTimes="0;0.5;1" dur="1s" repeatCount="indefinite"></animateTransform>
                            </circle>
                        </g>
                        <g transform="translate(150 50)">
                            <circle cx="0" cy="0" r="15" fill="#99c959">
                                <animateTransform attributeName="transform" type="scale" begin="0s" calcMode="spline"
                                    keySplines="0.3 0 0.7 1;0.3 0 0.7 1" values="0;1;0" keyTimes="0;0.5;1" dur="1s"
                                    repeatCount="indefinite"></animateTransform>
                            </circle>
                        </g>
                    </svg>
                </button>
                <button id="clearConv"><svg style="margin:0 auto;display:block" role="img" width="20px" height="20px"
                        viewBox="0 0 24 24">
                        <title>失忆</title>
                        <path fill="currentColor"
                            d="M8 20v-5h2v5h9v-7H5v7h3zm-4-9h16V8h-6V4h-4v4H4v3zM3 21v-8H2V7a1 1 0 0 1 1-1h5V3a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v3h5a1 1 0 0 1 1 1v6h-1v8a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1z">
                        </path>
                    </svg></button>
            </div>
        </div>
        <div style="display: none">
            <svg>
                <symbol viewBox="0 0 24 24" id="optionIcon">
                    <path fill="currentColor"
                        d="M12 3c-1.1 0-2 .9-2 2s.9 2 2 2s2-.9 2-2s-.9-2-2-2zm0 14c-1.1 0-2 .9-2 2s.9 2 2 2s2-.9 2-2s-.9-2-2-2zm0-7c-1.1 0-2 .9-2 2s.9 2 2 2s2-.9 2-2s-.9-2-2-2z">
                    </path>
                </symbol>
                <symbol viewBox="0 0 24 24" id="refreshIcon">
                    <path fill="currentColor"
                        d="M18.537 19.567A9.961 9.961 0 0 1 12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10c0 2.136-.67 4.116-1.81 5.74L17 12h3a8 8 0 1 0-2.46 5.772l.997 1.795z">
                    </path>
                </symbol>
                <symbol viewBox="0 0 24 24" id="halfRefIcon">
                    <path fill="currentColor"
                        d="M 4.009 12.163 C 4.012 12.206 2.02 12.329 2 12.098 C 2 6.575 6.477 2 12 2 C 17.523 2 22 6.477 22 12 C 22 14.136 21.33 16.116 20.19 17.74 L 17 12 L 20 12 C 19.999 5.842 13.333 1.993 7.999 5.073 C 3.211 8.343 4.374 12.389 4.009 12.163 Z" />
                </symbol>
                <symbol viewBox="-2 -2 20 20" id="copyIcon">
                    <path fill="currentColor"
                        d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z">
                    </path>
                    <path fill="currentColor"
                        d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z">
                    </path>
                </symbol>
                <symbol viewBox="0 0 24 24" id="delIcon">
                    <path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
                        stroke-width="2"
                        d="M9 7v0a3 3 0 0 1 3-3v0a3 3 0 0 1 3 3v0M9 7h6M9 7H6m9 0h3m2 0h-2M4 7h2m0 0v11a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V7">
                    </path>
                </symbol>
            </svg>
        </div>
    </div>
    <script>
        const messagsEle = document.getElementsByClassName("messages")[0];
        const chatlog = document.getElementById("chatlog");
        const stopEle = document.getElementById("stopChat");
        const sendBtnEle = document.getElementById("sendbutton");
        const textarea = document.getElementById("chatinput");
        const dialogEle = document.getElementById("setDialog");
        const speechOptionEle = document.getElementById("speechOption");
        textarea.focus();
        textarea.oninput = (e) => {
            if (textarea.value.trim().length) {
                sendBtnEle.classList.add("activeSendBtn");
            } else {
                sendBtnEle.classList.remove("activeSendBtn");
            }
            textarea.style.height = "47px";
            textarea.style.height = textarea.scrollHeight + "px";
        };
    </script>
    <script src="js/markdown-it.min.js"></script>
    <script src="js/highlight.min.js"></script>
    <script src="js/katex.min.js"></script>
    <script src="js/texmath.js"></script>
    <script src="js/markdown-it-link-attributes.min.js"></script>
    <script>
        const API_URL = "chat";
        let data = [];
        let input_str;
        let uuid = "";
        let loading = false;
        const scrollToBottom = () => {
            if (messagsEle.scrollHeight - messagsEle.scrollTop - messagsEle.clientHeight < 128) {
                messagsEle.scrollTo(0, messagsEle.scrollHeight)
            }
        }
        const scrollToBottomLoad = (ele) => {
            if (messagsEle.scrollHeight - messagsEle.scrollTop - messagsEle.clientHeight < ele.clientHeight + 128) {
                messagsEle.scrollTo(0, messagsEle.scrollHeight)
            }
        }
        const closeEvent = (ev) => {
            if (!dialogEle.contains(ev.target)) {
                dialogEle.style.display = "none";
                document.removeEventListener("mousedown", closeEvent, true);
            }
        }
        const md = markdownit({
            linkify: true, // 识别链接
            highlight: function (str, lang) { // markdown高亮
                try {
                    return hljs.highlightAuto(str).value;
                } catch (e) { }
                return ""; // use external default escaping
            }
        });
        md.use(texmath, {engine: katex, delimiters: "dollars", katexOptions: {macros: {"\\RR": "\\mathbb{R}"}}})
            .use(markdownitLinkAttributes, {attrs: {target: "_blank", rel: "noopener"}});
        const x = {
            getCodeLang(str = "") {
                const res = str.match(/ class="language-(.*?)"/);
                return (res && res[1]) || "";
            },
            getFragment(str = "") {
                return str ? `<span class="u-mdic-copy-code_lang">${str}</span>` : "";
            },
        };
        const strEncode = (str = "") => {
            if (!str || str.length === 0) return "";
            return str
                .replace(/</g, '&lt;')
                .replace(/>/g, '&gt;')
                .replace(/'/g, '\'')
                .replace(/"/g, '&quot;');
        };
        const getCodeLangFragment = (oriStr = "") => {
            return x.getFragment(x.getCodeLang(oriStr));
        };
        const copyClickCode = (ele) => {
            const input = document.createElement("textarea");
            input.value = ele.dataset.mdicContent;
            const nDom = ele.previousElementSibling;
            const nDelay = ele.dataset.mdicNotifyDelay;
            const cDom = nDom.previousElementSibling;
            document.body.appendChild(input);
            input.select();
            input.setSelectionRange(0, 9999);
            document.execCommand("copy");
            document.body.removeChild(input);
            if (nDom.style.display === "none") {
                nDom.style.display = "block";
                cDom && (cDom.style.display = "none");
                setTimeout(() => {
                    nDom.style.display = "none";
                    cDom && (cDom.style.display = "block");
                }, nDelay);
            }
        };
        const copyClickMd = (idx) => {
            const input = document.createElement("textarea");
            input.value = data[idx].content;
            document.body.appendChild(input);
            input.select();
            input.setSelectionRange(0, 9999);
            document.execCommand("copy");
            document.body.removeChild(input);
        }
        const enhanceCode = (render, options = {}) => (...args) => {
            /* args = [tokens, idx, options, env, slf] */
            const {
                btnText = "复制代码", // button text
                successText = "复制成功", // copy-success text
                successTextDelay = 2000, // successText show time [ms]
                showCodeLanguage = true, // false | show code language
            } = options;
            const [tokens = {}, idx = 0] = args;
            const cont = strEncode(tokens[idx].content || "");
            const originResult = render.apply(this, args);
            const langFrag = showCodeLanguage ? getCodeLangFragment(originResult) : "";
            const tpls = [
                '<div class="m-mdic-copy-wrapper">',
                `${langFrag}`,
                `<div class="u-mdic-copy-notify" style="display:none;">${successText}</div>`,
                '<button ',
                'class="u-mdic-copy-btn j-mdic-copy-btn" ',
                `data-mdic-content="${cont}" `,
                `data-mdic-notify-delay="${successTextDelay}" `,
                `onclick="copyClickCode(this)">${btnText}</button>`,
                '</div>',
            ];
            const LAST_TAG = "</pre>";
            const newResult = originResult.replace(LAST_TAG, `${tpls.join("")}${LAST_TAG}`);
            return newResult;
        };
        const codeBlockRender = md.renderer.rules.code_block;
        const fenceRender = md.renderer.rules.fence;
        md.renderer.rules.code_block = enhanceCode(codeBlockRender);
        md.renderer.rules.fence = enhanceCode(fenceRender);
        md.renderer.rules.image = function (tokens, idx, options, env, slf) {
            var token = tokens[idx];
            token.attrs[token.attrIndex("alt")][1] = slf.renderInlineAsText(token.children, options, env);
            token.attrSet("onload", "scrollToBottomLoad(this);this.removeAttribute('onload')");
            return slf.renderToken(tokens, idx, options)
        }
        let delayId;
        const delay = () => {
            return new Promise((resolve) => delayId = setTimeout(resolve, 100));
        }
        const confirmAction = (prompt) => {
            if (window.confirm(prompt)) {
                return true;
            }
            else {
                return false;
            }
        }
        const mdOptionEvent = (ev) => {
            let id = ev.target.dataset.id;
            if (id) {
                let parent = ev.target.parentElement;
                let idxEle = parent.parentElement;
                let idx = Array.prototype.indexOf.call(chatlog.children, (id === "refreshMd") ? idxEle.parentElement : idxEle.parentElement.parentElement);
                if (id === "refreshMd") {
                    if (!loading && chatlog.children[idx].dataset.loading !== "true") {
                        let className = parent.className;
                        if (className == "refreshReq") {
                            chatlog.children[idx].children[0].innerHTML = "<br />";
                            chatlog.children[idx].dataset.loading = true;
                            data[idx].content = "";
                            if (idx === currentVoiceIdx) {endSpeak()};
                            loadAction(true);
                            refreshIdx = idx;
                            streamGen();
                        } else {
                            chatlog.children[idx].dataset.loading = true;
                            progressData = data[idx].content;
                            loadAction(true);
                            refreshIdx = idx;
                            streamGen();
                        }
                    }
                } else if (id === "copyMd") {
                    idxEle.classList.add("moreOptionHidden");
                    copyClickMd(idx);
                } else if (id === "delMd") {
                    idxEle.classList.add("moreOptionHidden");
                    if (!loading) {
                        if (confirmAction("是否删除此消息?")) {
                            if (currentVoiceIdx) {
                                if (currentVoiceIdx === idx) {endSpeak()}
                                else if (currentVoiceIdx > idx) {currentVoiceIdx -= 1}
                            }
                            chatlog.removeChild(chatlog.children[idx]);
                            data.splice(idx, 1);
                        }
                    }
                }
            }
        }
        const moreOption = (ele) => {
            ele.classList.remove("moreOptionHidden");
        }
        const formatMdEle = (ele) => {
            let realMd = document.createElement("div");
            realMd.className = "markdown-body";
            ele.appendChild(realMd);
            let mdOption = document.createElement("div");
            mdOption.className = "mdOption";
            ele.appendChild(mdOption);
            if (ele.className !== "request") {
                mdOption.innerHTML = `<div class="refreshReq">
                    <svg data-id="refreshMd" width="16" height="16" role="img"><title>刷新</title><use xlink:href="#refreshIcon" /></svg>
                    <svg data-id="refreshMd" width="16" height="16" role="img"><title>继续</title><use xlink:href="#halfRefIcon" /></svg>
                </div>`
            }
            mdOption.innerHTML += `<div class="moreOption" onmouseenter="moreOption(this)">
            <svg class="optionTrigger" width="16" height="16" role="img"><title>选项</title><use xlink:href="#optionIcon" /></svg>
            <div class="optionItems" style="width:63px;left:-63px">` + `<div data-id="delMd" class="optionItem" title="删除">
                <svg width="20" height="20"><use xlink:href="#delIcon" /></svg>
            </div>
            <div data-id="copyMd" class="optionItem" title="复制">
                <svg width="20" height="20"><use xlink:href="#copyIcon" /></svg>
            </div></div></div>`;
            mdOption.onclick = mdOptionEvent;
        }
        let controller;
        let controllerId;
        let refreshIdx;
        let currentResEle;
        let progressData = "";
        let resStr = "";
        const reqWord = async (refresh) => {
            if (uuid == "") {
                uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
                    var r = Math.random() * 16 | 0,
                        v = c == 'x' ? r : (r & 0x3 | 0x8);
                    return v.toString(16);
                });
            }

            let headers = {"Content-Type": "application/json", "uuid" : uuid};
            let idx = refresh ? refreshIdx : data.length;
            let dataSlice = [data[idx - 1]];
            const res = await fetch(API_URL, {
                method: "POST",
                headers,
                body: input_str,
                signal: controller.signal
            });
            clearTimeout(controllerId);
            controllerId = void 0;
            if (res.status !== 200) {
                alert("请先启动服务！");
                stopLoading();
                return;
            }
            const decoder = new TextDecoder();
            const reader = res.body.getReader();
            const readChunk = async () => {
                return reader.read().then(async ({value, done}) => {
                    if (!done) {
                        resStr = decoder.decode(value);
                        let end = resStr.indexOf("<eop>");
                        value = resStr;
                        if (end > -1) {
                            value = resStr.substr(0, end)
                        }
                        currentResEle.children[0].innerHTML = md.render(value);
                        scrollToBottom();
                        return;
                    }
                });
            };
            await readChunk();
        };
        const streamGen = async () => {
            controller = new AbortController();
            controllerId = setTimeout(() => {
                alert("请求超时，请稍后重试！");
                stopLoading();
            }, 30000);
            let isRefresh = refreshIdx !== void 0;
            if (isRefresh) {
                currentResEle = chatlog.children[refreshIdx];
            } else if (!currentResEle) {
                currentResEle = document.createElement("div");
                currentResEle.className = "response";
                chatlog.appendChild(currentResEle);
                formatMdEle(currentResEle);
                currentResEle.children[0].innerHTML = "<br />";
                currentResEle.dataset.loading = true;
                scrollToBottom();
            }
            resStr = "";
            while(true) {
                await reqWord(isRefresh)
                await sleep(0)
                if (resStr.indexOf("<eop>") > -1) {
                    break;
                }
            }
            stopLoading(false);
        };
        const loadAction = (bool) => {
            loading = bool;
            sendBtnEle.disabled = bool;
            sendBtnEle.className = bool ? " loading" : "loaded";
            stopEle.style.display = bool ? "flex" : "none";
        }
        const stopLoading = (abort = true) => {
            stopEle.style.display = "none";
            if (abort) {
                controller.abort();
                if (controllerId) clearTimeout(controllerId);
                if (delayId) clearTimeout(delayId);
                if (refreshIdx !== void 0) {data[refreshIdx].content = progressData}
                else if (data[data.length - 1].role === "assistant") {data[data.length - 1].content = progressData}
                else {data.push({role: "assistant", content: progressData})}
            }
            controllerId = delayId = refreshIdx = void 0;
            currentResEle.dataset.loading = false;
            currentResEle = null;
            progressData = "";
            loadAction(false);
        }
        function sleep(time) {
            return new Promise(resolve => {
                setTimeout(() => {
                resolve();
                }, time);
            });
        }
        const generateText = async (message) => {
            loadAction(true);
            let request = document.createElement("div");
            request.className = "request";
            chatlog.appendChild(request);
            formatMdEle(request);
            request.children[0].innerHTML = md.render(message);
            data.push({role: "user", content: message});
            input_str = message;
            scrollToBottom();
            await streamGen();
        };
        textarea.onkeydown = (e) => {
            if (e.keyCode === 13) {
                if (!e.shiftKey) {
                    e.preventDefault();
                    genFunc();
                }
            }
        };
        const genFunc = function () {
            let message = textarea.value.trim();
            if (message.length !== 0) {
                if (loading === true) return;
                textarea.value = "";
                textarea.style.height = "47px";
                generateText(message);
            }
        };
        sendBtnEle.onclick = genFunc;
        stopEle.onclick = stopLoading;
        document.getElementById("clearConv").onclick = () => {
            if (!loading) {
				input_str = "reset";
				streamGen();
				chatlog.innerHTML = "<div></div>"
				scrollToBottom();
            }
        }
    </script>
    <link href="css/github-markdown-light.min.css" rel="stylesheet">
    <link href="css/github.min.css" rel="stylesheet">
    <link href="css/katex.min.css" rel="stylesheet">
    <link href="css/texmath.css" rel="stylesheet">
</body>
</html>
